package com.unitd.filesystem.provider.impl;

import com.unitd.filesystem.comm.FileItem;
import com.unitd.filesystem.comm.FileType;
import com.unitd.filesystem.comm.fdfs.FdfsSetting;
import com.unitd.filesystem.provider.AbstractProvider;
import com.unitd.filesystem.utils.HttpDownloadHelper;
import com.unitd.filesystem.utils.HttpFilePathHelper;
import com.unitd.frame.comm.utils.FileUtils;
import com.unitd.frame.comm.utils.StringUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @desc 与fastDFS文件系统进行上传、下载处理的接口实现类
 * @filename FdfsProvider.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/3/6
 */
public class FdfsProvider extends AbstractProvider {

	private static Logger logger = LoggerFactory.getLogger(FdfsProvider.class.getName());
	public static final String NAME = "fastDFS";

	private FdfsSetting setting;
	// 是否进行了初始化加载
	private static StorageClient1 storageClient1 = null;
	private static AtomicBoolean inited = new AtomicBoolean(false);

	/**
	 * @desc 构造方法,初始化Fdfs客户端
	 * @param urlprefix 文件前缀
	 * @param setting Fdfs设置对象
	 */
	public FdfsProvider(String urlprefix, FdfsSetting setting) {
		this.urlprefix = urlprefix.endsWith(DIR_SPLITER) ? urlprefix : urlprefix + DIR_SPLITER;
		this.setting = setting;

		if (!inited.get()) {
			inited.set(true);
			System.out.println("进入到初始化fastDFS连接方法了!");
			try {
				// 连接超时的时限，单位为毫秒
				ClientGlobal.setG_connect_timeout(setting.getConnectTimeout());
				// 网络超时的时限，单位为毫秒
				ClientGlobal.setG_network_timeout(setting.getNetworkTimeout());
				// 是否启用防盗链Token
				ClientGlobal.setG_anti_steal_token(setting.getAntiStealToken());
				// 字符集
				ClientGlobal.setG_charset(setting.getCharset());
				// 连接密钥
				ClientGlobal.setG_secret_key(setting.getSecretKey());
				// HTTP访问服务的端口号
				ClientGlobal.setG_tracker_http_port(setting.getTrackerHttpPort());
				// Tracker服务器列表
				ClientGlobal.setG_tracker_group(
						new TrackerGroup(setting.getTrackerGroup()));

				/* 获取fastDFS连接对象 */
				TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
				TrackerServer trackerServer = trackerClient.getConnection();
				if (trackerServer == null) {
					logger.error("getConnection return null");
				}
				StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
				if (storageServer == null) {
					logger.error("getStoreStorage return null");
				}
				storageClient1 = new StorageClient1(trackerServer, storageServer);
			} catch (Exception e) {
				logger.error(e.getMessage());
				processException(e);
			}
		}
	}

	/**
	 * @desc 文件方式上传
	 * @param catalog  图片分组(fastDFS中暂时用不到,主要用于七牛存储)
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param file     需要上传的文件
	 * @return 上传成功后的文件ID 即fileId
	 */
	@Override
	public String upload(String catalog, String fileName, File file) {
		if(!StringUtils.isNotBlank(fileName)) fileName = file.getName();

		InputStream fis;
		try {
			fis = new FileInputStream(file);
			int len = fis.available();
			byte[] fileBuff = new byte[len];
			fis.read(fileBuff);

			return this.upload(catalog, fileName, fileBuff, HttpFilePathHelper.parseFileType(file.getName()));
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return null;
		}
	}

	/**
	 * @desc 字节流方式上传
	 * @param catalog  图片分类
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param is       文件输入流
	 * @param fileType 文件类型信息
	 * @return 上传成功后的文件ID 即fileId
	 */
	@Override
	public String upload(String catalog, String fileName, InputStream is, FileType fileType) {
		try {
			byte[] fileBuff = null;
			if (is != null) {
				int len = is.available();
				fileBuff = new byte[len];
				is.read(fileBuff);
			}

			return upload(catalog, fileName, fileBuff, fileType);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return null;
		}finally{
			if (is != null){
				try {
					is.close();
				} catch (IOException e) {
					logger.error(e.getMessage());
					processException(e);
				}
			}
		}
	}

	/**
	 * @desc 字节方式上传
	 * @param catalog  图片分组
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param data     文件字节
	 * @param fileType 文件类型信息
	 * @return 上传成功后的文件ID 即fileId
	 */
	@Override
	public String upload(String catalog, String fileName, byte[] data, FileType fileType) {
		String fileId = null;
		try {
			NameValuePair[] metaList = new NameValuePair[0];
			fileId = storageClient1.upload_file1(data, FileUtils.getFileExt(fileType.getSuffix()) , metaList);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
		}
		return urlprefix + fileId;
	}

	/**
	 * @desc 从网络地址下载图片并上传
	 * @param catalog  图片分类
	 * @param fileName fileName 文件名(需确保唯一性),为空则自动生成
	 * @param origUrl  原始网络地址
	 * @return 上传成功后的文件ID 即fileId
	 */
	@Override
	public String upload(String catalog, String fileName, String origUrl) {
		String saveUrl = null;
		try {
			// 读取互联网上指定路径的文件/图片
			FileItem fileItem = HttpDownloadHelper.read(origUrl);
			saveUrl = this.upload(catalog, fileName, fileItem.getDatas(), fileItem.getFileType());
		} catch (IOException e) {
			logger.error(e.getMessage());
			processException(e);
		}
		return saveUrl;
	}

	/**
	 * @desc 根据文件名即fileId, 删除文件系统中的文件(上传文件时直接将fileId保存在了数据库中)
	 * @param fileName 指定的文件系统中文件
	 * file_id源码中的解释file_id the file id(including group name and filename);例如group1/M00/00/00/wKh2DVi_1HeAZiDmAAJ67kXDeNg657.jpg
	 * @return 0为成功，非0为失败，具体为错误代码
	 */
	@Override
	public boolean delete(String fileName) {
		try {
			int result = storageClient1.delete_file1(fileName);
			if(result > 0) return true;
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return false;
		}
		return false;
	}

	/**
	 * @desc 下载文件, 将分布式文件系统中的指定ID的文件下载到本地指定路径
	 * @param fileId 分布式文件系统中的除去文件访问前缀URL的文件路径
	 * @param destFile 下载的文件存放在本地的路径
	 */
	@Override
	public void download(String fileId, File destFile) {
		try {
			InputStream inputStream = downloadFile(fileId);
			FileUtils.copyInputStreamToFile(inputStream, destFile);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
		}
	}

	/**
	 * @desc 创建上传文件锁, 用于防止上传过程中文件发生改变(此方法暂时不需事先)
	 * @param fileNames 指定的文件系统中文件
	 * @return token路径
	 */
	@Override
	public String createUploadToken(String... fileNames) {
		return null;
	}

	/**
	 * @desc 定义的抽象方法, 用于获取使用的第三方的分布式文件系统的名称(比如: fastDFS、qiniu等)
	 * @return 分布式文件服务名称
	 */
	@Override
	public String name() {
		return NAME;
	}

	@Override
	public void close() throws IOException {
	}

	/**
	 * @desc 下载文件,将分布式文件系统中的指定ID的文件读取到文件流中
	 * @param fileId 分布式文件系统中的除去文件访问前缀URL的文件路径
	 * @return 返回一个流
	 */
	private InputStream downloadFile(String fileId) {
		try {
			if(fileId.contains(urlprefix)) {
				// 由于FastDFS提供的下载文件的方法需要对文件URL截取掉访问的http即nginx代理的路径,所以这里需要进行转换
				fileId = fileId.substring(setting.getUrlprefix().length()+1);
			}
			byte[] bytes = storageClient1.download_file1(fileId);
			return new ByteArrayInputStream(bytes);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
		}
		return null;
	}

	public FdfsSetting getSetting() {
		return setting;
	}

	public void setSetting(FdfsSetting setting) {
		this.setting = setting;
	}
}